﻿using System;
using System.Collections.Generic;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Transfer.Utils;

namespace Transfer.CrossFloor
{
	public class AutoCopyEntitiesManager
	{
		public AutoCopyEntitiesManager(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
		}

		public void Copy(Autodesk.Revit.DB.View disView, Transform transform, List<List<ElementId>> generalElements, bool copyWindows, bool copyDoors, bool copyOpenings)
		{
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Autodesk.Revit.DB.View activeView = this.m_Revit.Application.ActiveUIDocument.ActiveView;
			Common.GetSortFloorViews(document, double.MinValue, double.MaxValue);
			double num = 0.0;
			double num2 = 0.0;
			Level level = null;
			Level nextLevel = null;
			bool viewHeight = this.GetViewHeight(document, activeView, ref num, ref level);
			bool viewHeight2 = this.GetViewHeight(document, disView, ref num2, ref nextLevel);
			foreach (List<ElementId> list in generalElements)
			{
				if (list.Count != 0)
				{
					List<ElementId> list2 = new List<ElementId>();
					this.CopyElementsCrossFloor(activeView, list, disView, transform, ref list2);
					if (viewHeight && viewHeight2)
					{
						double viewHeigthDifference = num2 - num;
						this.EditElevationForNewElements(list, list2, viewHeigthDifference, nextLevel);
					}
					this.DeleteBaseWallElements(list2, copyWindows, copyDoors, copyOpenings);
				}
			}
		}

		private bool CopyElementsCrossFloor(Autodesk.Revit.DB.View fromView, List<ElementId> elementIds, Autodesk.Revit.DB.View toView, Transform tran, ref List<ElementId> newElementIds)
		{
			using (Transaction transaction = new Transaction(this.m_Revit.Application.ActiveUIDocument.Document, "copy elements"))
			{
				transaction.Start();
				try
				{
					ICollection<ElementId> collection = RevitVersionFuncs.CopyElements(fromView, elementIds, toView, tran);
					newElementIds.AddRange(collection);
				}
				catch (Exception ex)
				{
					string message = ex.Message;
					transaction.RollBack();
					return false;
				}
				FailureHandlingOptions failureHandlingOptions = transaction.GetFailureHandlingOptions();
				failureHandlingOptions.SetFailuresPreprocessor(new HidePasteDuplicateTypesPreprocessor());
				transaction.Commit(failureHandlingOptions);
			}
			return true;
		}

		private bool GetViewHeight(Autodesk.Revit.DB.Document doc, Autodesk.Revit.DB.View view, ref double height, ref Level nextLevel)
		{
			List<Autodesk.Revit.DB.View> sortFloorViews = Common.GetSortFloorViews(doc, double.MinValue, double.MaxValue);
			int num = sortFloorViews.FindIndex((Autodesk.Revit.DB.View s) => s.Id.Compare(view.Id) == 0);
			if (num == -1 || num == sortFloorViews.Count - 1)
			{
				return false;
			}
			int i = num + 1;
			double elevation = sortFloorViews[num].GenLevel.Elevation;
			while (i < sortFloorViews.Count)
			{
				double elevation2 = sortFloorViews[i].GenLevel.Elevation;
				if (!Geometry.IsEqual(elevation2, elevation))
				{
					nextLevel = sortFloorViews[i].GenLevel;
					height = elevation2 - elevation;
					break;
				}
				i++;
			}
			return true;
		}

		private void EditElevationForNewElements(List<ElementId> oldElementIdList, List<ElementId> newElementIdList, double viewHeigthDifference, Level nextLevel)
		{
			if (oldElementIdList.Count != newElementIdList.Count)
			{
				return;
			}
			if (Geometry.IsEqual(viewHeigthDifference, 0.0))
			{
				return;
			}
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			Transaction transaction = new Transaction(document);
			transaction.Start("change elevation");
			for (int i = 0; i < oldElementIdList.Count; i++)
			{
				Element element = document.GetElement(newElementIdList[i]);
				if (element != null)
				{
					if (element.GetType() == typeof(Wall))
					{
						Wall oldWall = element as Wall;
						Wall newWall = document.GetElement(newElementIdList[i]) as Wall;
						this.EditElevationForNewWall(oldWall, newWall, viewHeigthDifference, nextLevel);
					}
					else if (element.GetType() == typeof(FamilyInstance) && (element.Category.Id.IntegerValue == -2000100 || element.Category.Id.IntegerValue == -2001330))
					{
						FamilyInstance oldColumn = element as FamilyInstance;
						FamilyInstance newColumn = document.GetElement(newElementIdList[i]) as FamilyInstance;
						this.EditElevationForNewColumn(oldColumn, newColumn, viewHeigthDifference, nextLevel);
					}
				}
			}
			transaction.Commit();
		}

		private void EditElevationForNewWall(Wall oldWall, Wall newWall, double viewHeigthDifference, Level nextLevel)
		{
			if (nextLevel == null)
			{
				return;
			}
			if (oldWall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId() != ElementId.InvalidElementId)
			{
				double num = oldWall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).AsDouble();
				newWall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(num + viewHeigthDifference);
			}
		}

		private void EditElevationForNewColumn(FamilyInstance oldColumn, FamilyInstance newColumn, double viewHeigthDifference, Level nextLevel)
		{
			if (nextLevel == null)
			{
				return;
			}
			double num = oldColumn.get_Parameter(BuiltInParameter.SCHEDULE_TOP_LEVEL_OFFSET_PARAM).AsDouble();
			newColumn.get_Parameter(BuiltInParameter.SCHEDULE_TOP_LEVEL_OFFSET_PARAM).Set(num + viewHeigthDifference);
		}

		private void DeleteBaseWallElements(List<ElementId> newElementIds, bool copyWindows, bool copyDoors, bool copyOpenings)
		{
			if (newElementIds.Count == 0)
			{
				return;
			}
			Document document = this.m_Revit.Application.ActiveUIDocument.Document;
			if (null == newElementIds[0] || ElementId.InvalidElementId == newElementIds[0])
			{
				return;
			}
			Element element = document.GetElement(newElementIds[0]);
			if (element == null || element.GetType() != typeof(Wall))
			{
				return;
			}
			List<ElementId> list = new List<ElementId>();
			foreach (ElementId elementId in newElementIds)
			{
				Wall wall = document.GetElement(elementId) as Wall;
				if (wall != null)
				{
					if (!copyWindows)
					{
						list.AddRange(this.GetBaseWallElements(document, wall, typeof(FamilyInstance), BuiltInCategory.OST_Windows));
					}
					if (!copyDoors)
					{
						list.AddRange(this.GetBaseWallElements(document, wall, typeof(FamilyInstance), BuiltInCategory.OST_Doors));
					}
					if (!copyOpenings)
					{
						list.AddRange(this.GetBaseWallElements(document, wall, typeof(Opening), BuiltInCategory.OST_SWallRectOpening));
						list.AddRange(this.GetBaseWallElements(document, wall, typeof(Opening), BuiltInCategory.OST_ArcWallRectOpening));
					}
				}
			}
			this.DeleteElements(document, list);
		}

		private void DeleteElements(Autodesk.Revit.DB.Document doc, List<ElementId> elements)
		{
			if (elements.Count == 0)
			{
				return;
			}
			using (Transaction transaction = new Transaction(doc, "delete elements"))
			{
				transaction.Start();
				foreach (ElementId elementId in elements)
				{
					try
					{
						doc.Delete(elementId);
					}
					catch (Exception)
					{
					}
				}
				transaction.Commit();
			}
		}

		private List<ElementId> GetBaseWallElements(Autodesk.Revit.DB.Document doc, Wall wall, Type type, BuiltInCategory category)
		{
			List<ElementId> list = new List<ElementId>();
			ElementCategoryFilter elementCategoryFilter = new ElementCategoryFilter(category);
			IEnumerable<Element> enumerable = new FilteredElementCollector(doc).OfClass(type).WherePasses(elementCategoryFilter).ToElements();
			ElementId elementId = wall.get_Parameter(BuiltInParameter.ID_PARAM).AsElementId();
			foreach (Element element in enumerable)
			{
				ElementId obj;
				if (element.GetType() == typeof(FamilyInstance))
				{
					obj = (element as FamilyInstance).Host.get_Parameter(BuiltInParameter.ID_PARAM).AsElementId();
				}
				else
				{
					if (!(element.GetType() == typeof(Opening)))
					{
						continue;
					}
					obj = (element as Opening).Host.Id;
				}
				if (elementId.Equals(obj))
				{
					list.Add(element.Id);
				}
			}
			return list;
		}

		private ExternalCommandData m_Revit;
	}
}
